7.1 定义
接口代表一种调用契约,是多个方法声明的集合。
在某些动态语言里,接口(interface)也被称作协议(protocol)。准备交互的双方,共同遵守事先约定的规则,使得在无须知道对方身份的情况下进行协作。接口要实现的是做什么,而不关心怎么做,谁来做。
接口解除了类型依赖,有助于减少用户可视方法,屏蔽内部结构和实现细节。似乎好处很多,但这并不意味着可以滥用接口,毕竟接口实现机制会有运行期开销。对于相同包,或者不会频繁变化的内部模块之间,并不需要抽象出接口来强行分离。接口最常见的使用场景,是对包外提供访问,或预留扩展空间。
Go接口实现机制很简洁,只要目标类型方法集内包含接口声明的全部方法,就被视为实现了该接口,无须做显示声明。当然,目标类型可实现多个接口。
换句话说,我们可以先实现类型,而后再抽象出所需接口。这种非侵入式设计有很多好处。举例来说:在项目前期就设计出最合理接口并不容易,而在代码重构,模块分拆时再分离出接口,用以解耦就很常见。另外,在使用第三方库时,抽象出所需接口,即可屏蔽太多不需要关注的内容,也便于日后替换。
从内部实现来看,接口自身也是一种结构类型,只是编译器会对其做出很多限制。
type iface struct{
tab *itab
data unsafe.Pointer
}
- 不能有字段。
- 不能定义自己的方法。
- 只能声明方法,不能实现。
- 可嵌入其他接口类型。
接口通常以er作为名称后缀,方法名是声明组成部分,但参数名可不同或省略。
type tester interface{
test()
string()string
}
type data struct{}
func(*data)test() {}
func(data)string()string{return"" }
func main() {
var d data
//var t tester=d // 错误:data does not implement tester
// (test method has pointer receiver)
var t tester= &d
t.test()
println(t.string())
}编译器根据方法集来判断是否实现了接口,显然在上例中只有*data才复合tester的要求。
如果接口没有任何方法声明,那么就是一个空接口(interface{}),它的用途类似面向对象里的根类型Object,可被赋值为任何类型的对象。
接口变量默认值是nil。如果实现接口的类型支持,可做相等运算。
func main() {
var t1,t2 interface{}
println(t1==nil,t1==t2)
t1,t2=100,100
println(t1==t2)
t1,t2=map[string]int{},map[string]int{}
println(t1==t2)
}
输出:
true true
true
panic:runtime error:comparing uncomparable type map[string]int可以像匿名字段那样,嵌入其他接口。目标类型方法集中必须拥有包含嵌入接口方法在内的全部方法才算实现了该接口。
嵌入其他接口类型,相当于将其声明的方法集导入。这就要求不能有同名方法,因为不支持重载。还有,不能嵌入自身或循环嵌入,那会导致递归错误。
type stringer interface{
string()string
}
type tester interface{
stringer // 嵌入其他接口
test()
}
type data struct{}
func(*data)test() {}
func(data)string()string{
return""
}
func main() {
var d data
var t tester= &d
t.test()
println(t.string())
}
超集接口变量可隐式转换为子集,反过来不行。
func pp(a stringer) {
println(a.string())
}
func main() {
var d data
var t tester= &d
pp(t) // 隐式转换为子集接口
var s stringer=t // 超级转换为子集
println(s.string())
//var t2 tester=s// 错误:stringer does not implement tester
// (missing test method)
}支持匿名接口类型,可直接用于变量定义,或作为结构字段类型。
type data struct{}
func(data)string()string{
return""
}
type node struct{
data interface{ // 匿名接口类型
string()string
}
}
func main() {
var t interface{ // 定义匿名接口变量
string()string
} =data{}
n:=node{
data:t,
}
println(n.data.string())
}